/**
 * The MOSES project, model based software engineering of networked embedded systems.
 * 
 * Copyright (C) 2010-2013, University of Applied Sciences Wildau,
 * Telematics Research Group, http://www.tm.th-wildau.de/research-group
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses>.
 * 
 * Contributors:
 *     University of Applied Sciences Wildau - initial API and implementation
 * 
 * If you are interested in a commercial license please contact us via
 * e-mail: ralf.vandenhouten@th-wildau.de.
 */
package de.thwildau.tm.moses.abnf.generator;

import com.google.common.base.Objects;
import com.google.common.collect.Iterators;
import com.google.inject.Inject;
import de.thwildau.tm.moses.abnf.abnf.Rule;
import de.thwildau.tm.moses.abnf.generator.ModelUtil;
import de.thwildau.tm.moses.abnf.util.EmptyTreeIterator;
import de.thwildau.tm.moses.xabnf.xabnf.GeneratorModel;
import de.thwildau.tm.moses.xabnf.xabnf.RuleBinding;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.Functions.Function0;
import org.eclipse.xtext.xbase.lib.IteratorExtensions;

/**
 * NameResolver class to retrieve names of Java classes, fields and methods for specific
 * ABNF elements.
 */
@SuppressWarnings("all")
public class NameResolver {
  private final static Logger LOG = new Function0<Logger>() {
    public Logger apply() {
      Logger _logger = Logger.getLogger(NameResolver.class);
      return _logger;
    }
  }.apply();
  
  @Inject
  private IQualifiedNameConverter converter;
  
  /**
   * Returns the XAbnf-Resource for the given ABNF-Resource. By convention the XAbnf-file must have
   * the same name as the Abnf-file (e.g. 'content.abnf' will search for 'content.xabnf'). The XAbnf-file
   * must be in the same directory. This method return null, if no XAbnf-Resource is found.
   */
  private Resource getXabnfResource(final Resource abnfResource) {
    URI _uRI = abnfResource.getURI();
    URI _trimFileExtension = _uRI.trimFileExtension();
    final String fileName = _trimFileExtension.lastSegment();
    final String newFile = (fileName + ModelUtil.XABNF_FILE_EXTENSION);
    URI _uRI_1 = abnfResource.getURI();
    URI _trimSegments = _uRI_1.trimSegments(1);
    final URI xabnfUri = _trimSegments.appendSegment(newFile);
    try {
      ResourceSet _resourceSet = abnfResource.getResourceSet();
      final Resource xabnfResource = _resourceSet.getResource(xabnfUri, true);
      boolean _equals = Objects.equal(xabnfResource, null);
      if (_equals) {
        URI _uRI_2 = abnfResource.getURI();
        String _devicePath = _uRI_2.devicePath();
        String _plus = ("Could not find XAbnf file for " + _devicePath);
        String _plus_1 = (_plus + ". Load was successful.");
        NameResolver.LOG.info(_plus_1);
      }
      return xabnfResource;
    } catch (final Throwable _t) {
      if (_t instanceof Exception) {
        final Exception e = (Exception)_t;
        URI _uRI_3 = abnfResource.getURI();
        String _devicePath_1 = _uRI_3.devicePath();
        String _plus_2 = ("Could not find XAbnf file for " + _devicePath_1);
        String _plus_3 = (_plus_2 + ". Load was failed.");
        NameResolver.LOG.info(_plus_3, e);
      } else {
        throw Exceptions.sneakyThrow(_t);
      }
    }
    return null;
  }
  
  public TreeIterator<EObject> getXabnfContent(final Resource resource) {
    TreeIterator<EObject> _xblockexpression = null;
    {
      final Resource xabnfResource = this.getXabnfResource(resource);
      TreeIterator<EObject> _xifexpression = null;
      boolean _notEquals = (!Objects.equal(xabnfResource, null));
      if (_notEquals) {
        TreeIterator<EObject> _allContents = xabnfResource.getAllContents();
        _xifexpression = _allContents;
      } else {
        EmptyTreeIterator<EObject> _emptyTreeIterator = new EmptyTreeIterator<EObject>();
        _xifexpression = _emptyTreeIterator;
      }
      _xblockexpression = (_xifexpression);
    }
    return _xblockexpression;
  }
  
  /**
   * Returns true, if the given resource is the ABNF-CoreRule file.
   */
  public boolean isAbnfCoreResource(final Resource resource) {
    URI _uRI = resource.getURI();
    String resourceName = _uRI.lastSegment();
    return resourceName.equals(ModelUtil.CORE_RULE_FILE);
  }
  
  /**
   * Returns the name of the target package, where the generated classes are located.
   * If there is an Xabnf-Resource for the given Abnf-Resource the 'package' attribute
   * of the GeneratorModel is consulted. If no 'package' was specified in the Xabnf-file
   * the package name is computed from the name of the resource.
   */
  public String getPackageName(final Resource resource) {
    String _xblockexpression = null;
    {
      TreeIterator<EObject> _xabnfContent = this.getXabnfContent(resource);
      QualifiedName defaultPackageName = this.getDefaultPackageName(_xabnfContent);
      boolean _notEquals = (!Objects.equal(defaultPackageName, null));
      if (_notEquals) {
        return defaultPackageName.toString();
      }
      URI _uRI = resource.getURI();
      String resourceName = _uRI.lastSegment();
      int _indexOf = resourceName.indexOf(".");
      String packageName = resourceName.substring(0, _indexOf);
      String _replace = packageName.replace("-", ".");
      _xblockexpression = (_replace);
    }
    return _xblockexpression;
  }
  
  /**
   * Returns the full-qualified name for the given ABNF rule.
   * The package name is computed from the resource (see getPackageName(Resource)).
   */
  public QualifiedName getFqn(final Rule rule) {
    QualifiedName _xblockexpression = null;
    {
      final Resource resource = rule.eResource();
      TreeIterator<EObject> _xabnfContent = this.getXabnfContent(resource);
      String _className = this.getClassName(rule, _xabnfContent);
      QualifiedName _fqn = this.getFqn(resource, _className);
      _xblockexpression = (_fqn);
    }
    return _xblockexpression;
  }
  
  /**
   * Returns the full-qualified name for the given ABNF resource and className.
   * The package name is computed from the resource (see getPackageName(Resource)).
   */
  public QualifiedName getFqn(final Resource resource, final String className) {
    String _packageName = this.getPackageName(resource);
    QualifiedName _qualifiedName = this.converter.toQualifiedName(_packageName);
    QualifiedName _append = _qualifiedName.append(className);
    return _append;
  }
  
  /**
   * Returns the full-qualified name for the given packageName and the className
   * 
   * @param packageName - the name of the package
   * @param className - the name of the class
   */
  public QualifiedName getFqn(final String packageName, final String className) {
    QualifiedName _qualifiedName = this.converter.toQualifiedName(packageName);
    QualifiedName _append = _qualifiedName.append(className);
    return _append;
  }
  
  /**
   * Returns the Java file for the given full-qualified name.
   */
  public String getJavaFilename(final QualifiedName fqn) {
    String _string = fqn.toString("/");
    String _plus = (_string + ".java");
    return _plus;
  }
  
  /**
   * Return the Java class name for the given ABNF rule. This method searches for RuleBindings in the 'content'
   * parameters and returns the 'targetRuleName' of a matching RuleBinding. If no RuleBinding match, the Java class
   * name is the name of the rule.
   */
  private String getClassName(final Rule rule, final TreeIterator<EObject> allContents) {
    String className = null;
    Iterator<RuleBinding> _filter = Iterators.<RuleBinding>filter(allContents, RuleBinding.class);
    Iterable<RuleBinding> _iterable = IteratorExtensions.<RuleBinding>toIterable(_filter);
    for (final RuleBinding rb : _iterable) {
      Rule _rule = rb.getRule();
      boolean _equals = _rule.equals(rule);
      if (_equals) {
        String _targetRuleName = rb.getTargetRuleName();
        className = _targetRuleName;
      }
    }
    boolean _equals_1 = Objects.equal(className, null);
    if (_equals_1) {
      String _name = rule.getName();
      className = _name;
    }
    boolean _equals_2 = Objects.equal(className, null);
    if (_equals_2) {
      String _plus = ("Could not find a name for rule " + rule);
      String _plus_1 = (_plus + ". Content size was ");
      List<EObject> _list = IteratorExtensions.<EObject>toList(allContents);
      int _size = _list.size();
      String _plus_2 = (_plus_1 + Integer.valueOf(_size));
      NameResolver.LOG.warn(_plus_2);
    }
    return className;
  }
  
  /**
   * Returns the default package name of the GeneratorModel
   */
  private QualifiedName getDefaultPackageName(final TreeIterator<EObject> allContents) {
    Iterator<GeneratorModel> _filter = Iterators.<GeneratorModel>filter(allContents, GeneratorModel.class);
    Iterable<GeneratorModel> _iterable = IteratorExtensions.<GeneratorModel>toIterable(_filter);
    for (final GeneratorModel gm : _iterable) {
      String _package = gm.getPackage();
      boolean _notEquals = (!Objects.equal(_package, null));
      if (_notEquals) {
        String _package_1 = gm.getPackage();
        return QualifiedName.create(_package_1);
      } else {
        return null;
      }
    }
    return null;
  }
}
